package com.luanye.enjoy.service.impl;

import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luanye.enjoy.constants.RedisConst;
import com.luanye.enjoy.constants.UserConst;
import com.luanye.enjoy.dto.SmsDTO;
import com.luanye.enjoy.dto.UserConfigDTO;
import com.luanye.enjoy.dto.UserDTO;
import com.luanye.enjoy.entity.User;
import com.luanye.enjoy.entity.UserRole;
import com.luanye.enjoy.entity.WeChatUser;
import com.luanye.enjoy.enums.ResultCode;
import com.luanye.enjoy.enums.WeChatResultCode;
import com.luanye.enjoy.exception.APIException;
import com.luanye.enjoy.mapper.UserMapper;
import com.luanye.enjoy.mapper.UserRoleMapper;
import com.luanye.enjoy.service.UserRoleService;
import com.luanye.enjoy.service.UserService;
import com.luanye.enjoy.utils.*;
import com.luanye.enjoy.vo.LoginVo;
import com.luanye.enjoy.vo.RespBean;
import lombok.AllArgsConstructor;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import static com.luanye.enjoy.constants.MQConst.SMS_EXCHANGE;
import static com.luanye.enjoy.constants.RedisConst.USER_PHONE_KEY;
import static com.luanye.enjoy.constants.SmsConst.TEMPLATE_PHONE_VER;
import static com.luanye.enjoy.enums.ResultCode.FAILED;


@Service("userService")
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final String appid = "wx23c2ff0e1bcf9e1c";

    private final String secret = "1eae0a09ff7b5ad13068828ab615b1d6";

    private final UserMapper userMapper;

    private final Executor taskPool;

    private final UserRoleService userRoleService;

    private final UserRoleMapper userRoleMapper;

    private final RedisCache redisCache;

    private final RedisTemplate redisTemplate;

    private final RabbitTemplate rabbitTemplate;
    @Override
    public RespBean wxLogin(String jscode) {
        JSONObject sessionInfo = JSONObject.parseObject(jscode2Session(jscode));
        return new RespBean(sessionInfo);
    }

    private String jscode2Session(String code) {
        String sessionInfo = null;
        try {
            sessionInfo = Jscode2SessionUtil.jscode2session(appid, secret, code, "authorization_code");
        } catch (Exception e) {
            throw new APIException(WeChatResultCode.ACQUIRE_OPENID_FAILED);
        }
        return sessionInfo;
    }

    @Override
    @Transactional
    public RespBean register(UserDTO userDTO, HttpServletRequest request) {
        String openid = userDTO.getOpenId();
        User origin = userMapper.getUserByOpenId(openid);
        if (Objects.nonNull(origin))
            return login(origin);
        //注册
        User user = BeanCopyUtils.copyBean(userDTO, User.class);
        save(user);
        //初始化角色
        CompletableFuture<Void> initRole = CompletableFuture.runAsync( () -> {
            userRoleService.save(new UserRole(user.getId(), UserConst.ROLE_USER));
        } , taskPool);

        //返回
        return login(user);
    }


    RespBean login(User user){
        Long userid = user.getId();
        //封装角色
        List<String> roleByUserId = userRoleMapper.getRoleByUserId(userid);
        Set authorities = new HashSet(roleByUserId);

        WeChatUser weChatUser = new WeChatUser()
                .setUser(user)
                .setAuthorities(authorities);
        //登陆
        redisTemplate.opsForValue().setIfAbsent(RedisConst.USER_LOGIN_KEY + user.getId(), weChatUser);

        String token = JwtUtil.createJWT(String.valueOf(userid));

        return RespBean.success(new LoginVo(token, user));
    }

    @Override
    public RespBean updateLoc(User user) {
        User newUser = userMapper.getUserByOpenId(user.getOpenId());
        user.setId(newUser.getId());
        userMapper.updateById(user);
        return RespBean.success();
    }

    @Override
    public RespBean updateUserConfig(UserConfigDTO dto) {
        Long userId = SecurityUtils.getUserId();
        UpdateWrapper<User> update = new UpdateWrapper<>();
        update.eq("id", userId);
        User user = BeanCopyUtils.copyBean(dto, User.class);
        boolean isSuccess = update(user, update);
        if (!isSuccess){
            return RespBean.error(FAILED);
        }
        return RespBean.success();
    }

    @Override
    public RespBean registerSms(String phone) {
        boolean isMobile = Validator.isMobile(phone);
        if (!isMobile){
            return RespBean.error(ResultCode.PHONE_NUM_ERROR);
        }

        String key = USER_PHONE_KEY + SecurityUtils.getUserId();

        if (Objects.nonNull(redisTemplate.opsForValue().get(key))) {
            return RespBean.error(ResultCode.NOT_FLUENT_OPERATE);
        }
        String code = VerCodeGenerateUtil.generateVerCode();
        SmsDTO dto = new SmsDTO(phone, new String[]{code}, TEMPLATE_PHONE_VER);

        rabbitTemplate.convertAndSend(SMS_EXCHANGE, "*", new Message(JSON.toJSONBytes(dto),new MessageProperties()));

        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
        return RespBean.success();
    }

    @Override
    public RespBean updatePhoneByCode(String phone, String code) {
        Long userId = SecurityUtils.getUserId();
        String key = USER_PHONE_KEY + userId;

        String code_origin = redisCache.getCacheObject(key);
        if (!code.equals(code_origin)){
            return RespBean.error(ResultCode.CODE_ERROR);
        }

        boolean isSuccess = update().setSql("phone = " + phone).eq("id", userId).update();
        if (!isSuccess){
            return RespBean.error(FAILED);
        }
        return RespBean.success();
    }
}
